package com.cloudkinto.utils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * cglib包中的bean拷贝(性能优于Spring当中的BeanUtils处理大量数据性能翻倍）
 *
 * @author long
 */
@Slf4j
public class BeanCopierUtils {


    /**
     * 创建一个map来存储BeanCopier缓存
     */
    private static final Map<String, BeanCopier> BEAN_COPIER_MAP = new ConcurrentHashMap<>();

    /**
     * 深拷贝,我们可以直接传实例化的拷贝对象和被实例化的拷贝对象进行深拷贝
     *
     * @param source 源对象
     * @param target 目标类
     */
    private static void copy(Object source, Object target) {
        // 获取当前两者转换map对应的key
        String key = getKey(source, target);
        BeanCopier beanCopier;
        // 判断键是否存在,不存在就将BeanCopier插入到map里,存在就直接获取
        if (!BEAN_COPIER_MAP.containsKey(key)) {
            // 参数1: 源对象类   参数2: 目标对象类   参数3: 是否使用自定义转换器
            beanCopier = BeanCopier.create(source.getClass(), target.getClass(), false);
            BEAN_COPIER_MAP.put(key, beanCopier);
        } else {
            beanCopier = BEAN_COPIER_MAP.get(key);
        }
        // 自定义转换器可在copy函数当中的第三个参数设置
        beanCopier.copy(source, target, null);
    }

    /**
     * 深拷贝
     *
     * @param source 源对象
     * @param target 目标类
     * @param <T>    目标类型
     * @return 单个目标类
     */
    public static <T> T copy(Object source, Class<T> target) {
        // 如果源对象为空,结束
        if (source == null) {
            return null;
        }
        // 用来判断目标类型空指针异常
        Objects.requireNonNull(target);
        T result = null;
        try {
            result = target.newInstance();
            copy(source, result);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * List深拷贝
     *
     * @param sources 源集合
     * @param target  目标类
     * @param <S>     源类型
     * @param <T>     目标类型
     * @return 目标类集合
     */
    public static <S, T> List<T> copyList(List<S> sources, Class<T> target) {
        // 用来判断目标类型空指针异常
        Objects.requireNonNull(target);
        return sources.stream().map(src -> copy(src, target)).collect(Collectors.toList());

    }

    /**
     * @param source    原数据，list集合
     * @param target    接收复制的数据实体类属性，会自动转换成list集合
     * @param sourceKey 原数据的实体类属性，为了防止空数据获取该属性报错
     * @param <O>       原数据类型
     * @param <T>       目标数据类型
     * @return
     */
    public static <O, T> List<T> listCopyMapper(Collection<O> source, Class<O> sourceKey, Class<T> target) {
        String baseKey = getKey(sourceKey, target);
        BeanCopier copier;
        if (!BEAN_COPIER_MAP.containsKey(baseKey)) {
            copier = BeanCopier.create(sourceKey, target, false);
            BEAN_COPIER_MAP.put(baseKey, copier);
        } else {
            copier = BEAN_COPIER_MAP.get(baseKey);
        }
        List<T> targets = new ArrayList<>(source.size());
        try {
            source.forEach(
                    bean -> {
                        T instance = null;
                        try {
                            instance = target.getDeclaredConstructor().newInstance();
                        } catch (IllegalAccessException | NoSuchMethodException | InvocationTargetException |
                                 InstantiationException e) {
                            e.printStackTrace();
                        }
                        copier.copy(bean, instance, null);
                        targets.add(instance);
                    }
            );
        } catch (Exception e) {
            log.error("list bean复制失败" + e.getMessage());
        }
        return targets;
    }

    /**
     * 自定义类型转换器
     *
     * @param source    源对象
     * @param target    目标类
     * @param converter 转换器
     */
    private static void copy(Object source, Object target, Converter converter) {
        if (!Objects.isNull(converter)) {
            BeanCopier beanCopier = BeanCopier.create(source.getClass(), target.getClass(), true);
            beanCopier.copy(source, target, converter);
        } else {
            copy(source, target);
        }
    }

    /**
     * 自定义类型转换器
     *
     * @param source    源对象
     * @param target    目标类
     * @param converter 转换器
     * @param <T>       目标类型
     * @return 单个目标类
     */
    public static <T> T copy(Object source, Class<T> target, Converter converter) {
        // 如果源对象为空,结束
        if (source == null) {
            return null;
        }
        // 用来判断目标类型空指针异常
        Objects.requireNonNull(target);
        T result = null;
        try {
            result = target.newInstance();
            copy(source, result, converter);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取Map中的Key
     *
     * @param source 源对象
     * @param target 目标类
     * @return 源对象与目标类名字的拼接
     */
    private static String getKey(Object source, Object target) {
        return ((Class<?>) source).getName() + "_" + ((Class<?>) target).getName();
    }

}